package com.ruoyi.common.core.dsl

import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.scheduling.config.CronTask
import org.springframework.scheduling.config.IntervalTask
import org.springframework.scheduling.config.ScheduledTask
import org.springframework.scheduling.config.ScheduledTaskRegistrar

import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicInteger


class AdapterExecutor {
    private static final Logger log = LoggerFactory.getLogger(AdapterContext.class);

    private static threadCount = new AtomicInteger(0)
    private static final ScheduledTaskRegistrar scheduler = new ScheduledTaskRegistrar()
    private static Map<String, ScheduledTask> scheduledFutures = new ConcurrentHashMap<>()
    def global = new ConcurrentHashMap()
    Map<String, Closure> api
    Map<String, AdapterRequest> resolvedApi = [:]

    private final DslSpec spec
    private final String name
    def httpClient
    def globalContext

    static {
        scheduler.scheduler = Executors.newScheduledThreadPool(4, { r ->
            def t = new Thread(r, "Delegate-Scheduler-${threadCount.getAndIncrement()}")
            t.setDaemon(true)
            t
        })
    }


    AdapterExecutor(DslSpec adapterSpec, String name, Map<String, Object> constants) {
        this.name = name
        if (constants) {
            global.putAll(constants)
        }
        cancelScheduledTask(name)
        spec = adapterSpec
        if (spec.givenCall) {
            spec.givenCall.delegate = this
            spec.givenCall()
        }

        //注意源码isEqual(value, newValue)
        def rules = []

        def delegate = this

        // 定时任务的delegate
        if (spec.scheduleCall) {
            spec.scheduleCall.delegate = globalContext
            def task
            def command = {
                def count = 0
                def retrys = global.getOrDefault('retrys', 3)
                def sleep = global.getOrDefault('sleep', 10000L)
                while (true && scheduledFutures.containsKey(name) && count <= retrys) {
                    try {
                        log.info("开始[{}]定时任务.", name)
                        spec.scheduleCall()
                        log.info("执行[{}]定时任务成功.", name)
                        break
                    } catch (Exception e) {
                        count += 1
                        log.error("执行[{}]定时任务失败, {}ms后进行第{}次重试", name, sleep, count, e)
                        Thread.sleep(sleep)
                    }
                }
            }
            try {
                if (spec.cron) {
                    task = scheduler.scheduleCronTask(new CronTask(command, spec.cron))
                    log.info("设置[{}](cron)定时任务成功.", name)
                } else {
                    task = scheduler.scheduleFixedDelayTask(new IntervalTask(command, spec.rate, 0))
                    log.info("设置[{}](delay)定时任务成功.", name)
                }
                scheduledFutures.put(name, task)
            } catch (Exception e) {
                log.error("设置[{}]定时任务失败.", name, e)
            }
        }
    }

    Object doAdapter(Map<String, Object> bodyFromController) {
        try {
            def context = new AdapterContext(name: name, global: global, input: bodyFromController, api: resolvedApi)

            return spec.executeCall.rehydrate(context, this, spec)()
        } catch (Exception e) {
            throw new RuntimeException(String.format("Adapter[%s]执行失败:%s.", name, e.getMessage()), e)
        }
    }

    static void cancelScheduledTask(String name) {
        if (scheduledFutures.remove(name)?.cancel()) {
            log.info("停止[{}]定时任务成功.", name)
        }
    }

    Closure getGiven() {
        return spec.givenCall
    }
}
